#include <iostream>
#include <unistd.h>
#include <vector>
#include <error.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>

using namespace std;
#define N 10

typedef void (*task)();
vector<task> vt; // 任务队列

// 自定义任务列表
void task1()
{
    cout << "检测当前角色健康状态" << endl;
}

void task2()
{
    cout << "检测当前角色物品补给" << endl;
}

void task3()
{
    cout << "检测当前角色生命值" << endl;
}

void task4()
{
    cout << "检测当前角色法力值" << endl;
}

void creator_task(vector<task> &vt)
{
    vt.push_back(task1);
    vt.push_back(task2);
    vt.push_back(task3);
    vt.push_back(task4);
}

// 子进程执行任务
void chlid_go()
{
    int task = 0;
    while (true)
    {
        int n = read(0, &task, sizeof(int)); // 读取的内容就是任务列表的下标
        if (n > 0)
        {
            cout << "处理该任务的进程是：" << getpid() << ":";
            vt[task](); // 根据读取到的下标去调用任务列表里的函数指针
        }
        else
            break;
    }
}

class process // 描述单个进程的类
{
public:
    process(int id, const string &name, int fd)
        : id_(id), name_(name), fd_(fd)
    {
    }
    ~process()
    {
    }

public:
    pid_t id_;    // 子进程id
    string name_; // 子进程名称
    int fd_;      // 控制子进程的管道写端(真正控制子进程就是写端的文件描述符)
};

int main()
{
    creator_task(vt);
    srand(time(0));
    vector<process> vpr;        // 管道子进程的数据结构
    vector<int> oldfd;          // 记录写端的文件描述符
    for (int i = 0; i < N; i++) // 创建十个子进程的进程池
    {
        int pipefd[2];
        int n = pipe(pipefd); // 父进程创建管道
        if (n == -1)
        {
            perror("pipe");
            exit(-1);
        }

        // 创建子进程
        pid_t id = fork();
        // 子进程执行流
        if (id == 0)
        {
            for (auto num : oldfd)
                close(num);     // 关闭新子进程继承父进程之前的写端
            close(pipefd[1]);   // 因为子进程只负责读数据，所以关闭子进程对管道的写端
            dup2(pipefd[0], 0); // 重定向，这一步只是方便后续的测试
            chlid_go();
            exit(0);
        }

        // 父进程执行流
        close(pipefd[0]); // 父进程只写，因此关闭父进程对管道的读端
        string pro_name = "子进程" + to_string(i);
        vpr.push_back({id, pro_name, pipefd[1]}); // 把子进程的信息插入到数据结构中
        oldfd.push_back(pipefd[1]);               // 记录父进程新打开的写端
    }

    for (auto &num : vpr) // 验证进程池里的进程
    {
        cout << num.name_ << ":" << num.id_ << ":" << num.fd_ << endl;
    }

    // 父进程开始派送任务
    cout << "主进程开始给子进程分配任务" << endl;
    sleep(2);

    for (int i = 0; i < 5; i++)
    {
        int proc_num = rand() % N;                        // 随机子进程-vpr下标
        int task_num = rand() % 4;                        // 随机任务
        write(vpr[proc_num].fd_, &task_num, sizeof(int)); // 分配任务的核心就是进程间通信
        sleep(1);
    }

    for (int i = 0; i < vpr.size(); i++)
    {
        close(vpr[i].fd_);
        waitpid(vpr[i].id_, nullptr, 0);
        cout<<"等待子进程:"<<vpr[i].name_<<endl;
        sleep(1);
    }

    return 0;
}